#include<Lcode/l_main.h>
#include<machine/m_bw.h>
#include<Lcode/l_dataflow.h>


#include<vector>
#include"LcodeBlockImpl.hpp"
#include"LcodeIDSpec.hpp"
#include"../cg/Operation.hpp"
#include"../share/predicat.h"
#include"../cg/common.hpp"

static DataOperand * build_data_operand(L_Operand* opd )
{
	assert(opd);
	Value * v = NULL;
	DataOperand * d = NULL;
	if(bw_is_gen_data_reg(opd))
	{
		v = new GenValue(LM_convert_id(opd->value.r));	
	}
	else if(bw_is_mac_data_reg(opd))
	{
		v = new MacroValue(LM_convert_macro(opd->value.mac));
	}
	else if(bw_is_imm(opd))
	{
		v = new ImmValue(opd->value.i);
	}
	else if(bw_is_label(opd))
	{
		v = new LabelValue(std::string(opd->value.l));
	}
	else
	{
		assert("error"== "the L_Operand is not data operand");
	}
	d = new DataOperand(v);
	return d;
}

// [begin + 1, end)
// bw_is_simd_start(begin)
// bw_is_simd_end(end)
static 
SimdOperand * build_simd_operand(L_Oper * begin, L_Oper* end, int i  ,bool is_src)
{
	L_Oper * oper = NULL;
	GenValue* vs[8];
	int count = 0; 
	for( oper = begin->next_op; oper != end; oper =oper->next_op)
	{
		count++;
		if(is_src)
		{
			assert(bw_is_gen_data_reg(oper->src[i]));
			vs[count] =new GenValue( LM_convert_id(oper->src[i]->value.r));
		}
		else 
		{
			assert(bw_is_gen_data_reg(oper->dest[i]));
			vs[count] = new GenValue(LM_convert_id(oper->dest[i]->value.r));
		}
	}
	assert(count == 8);
	return new SimdOperand(std::vector<RegValue*>(vs, vs+8));
}

AddrOperand * build_addr_operand(L_Operand* opd)
{
	assert(opd);
	Value * v = NULL;
	if(bw_is_gen_addr_reg(opd))
	{
		v = new GenValue(LM_convert_id(opd->value.r));	
	}
	else if(bw_is_mac_addr_reg(opd))
	{
		v = new MacroValue(LM_convert_macro(opd->value.mac));
	}
	else if(bw_is_imm(opd))
	{
		v = new GenValue(opd->value.i);
	}
	else if(bw_is_label(opd))
	{
		v = new LabelValue(std::string(opd->value.l));
	}
	else
		assert("error" == "addr operand is expected");

	return new AddrOperand(v);
}

void static bw_skip_simd_oper(L_Oper *& oper)
{
	while(!bw_is_simd_end(oper))
	{
		oper = oper->next_op;
	}
}


enum BLKType  LcodeBlockImpl::get_block_type()const
{
	if(bw_is_unroll_cb(cb))
		return UNROLL_LOOP_BT;
	if(bw_is_loop_header(cb))
		return PREHEADER_BT;
	if(bw_is_loop_remainder(cb))
		return REMAINDER_BT;
	if(cb->flags & L_CB_EPILOGUE)
		return ENTRY_BT;
	if(cb->flags & L_CB_PROLOGUE)
		return EXIT_BT;
	return NORMAL_BT;
}

template<unsigned int >
Node * make_operation(L_Oper *oper, L_Cb * cb)
{
	assert("error" == "the function should never be instance ");
}

template<>
Node * make_operation<NORMAL_OPER> (L_Oper *oper, L_Cb *cb)
{
	DataOperand* src_opd[2] ;
	for(int i = 0; i < 2; i++)
	{
		if(oper->src[i] != NULL)
			src_opd[i] = build_data_operand(oper->src[i]);
		else
			assert("error" == "the src operand is expected");
	}
	DataOperand *dest_opd[1];	
	for(int i = 0; i < 1; i++)
	{
		if(oper->dest[i] != NULL)
			dest_opd[i] = build_data_operand(oper->src[i]);	
	}
	return new Operation<NORMAL_OPER>(oper->proc_opc, dest_opd[0], src_opd[0], src_opd[1]);
}

template<>
Node* make_operation<LOAD>(L_Oper* oper, L_Cb * cb)
{
	AddrOperand * src[3] ;
	DataOperand * dest[1];	
	for(int i = 0; i < 3;i++)
	{
		if(oper->src[i] == NULL)
			src[i] = build_addr_operand(oper->src[i]);
	} 
	for(int i = 0; i < 1; i++)
	{
		if(oper->dest[i] == NULL)
			dest[i] = build_data_operand(oper->dest[i]);
	}
	return new Operation<LOAD>(oper->proc_opc, dest[0], src[0], src[1], src[2]);
}

template<>
Node* make_operation<STORE>(L_Oper* oper, L_Cb *cb)
{
	AddrOperand * addr[3];
	DataOperand * data[1];
	for(int i = 0; i < 1; i++)
	{
		data[i] = build_data_operand(oper->src[i]);
	}
	for(int i =1; i < 4; i++)
	{
		addr[i-1] = build_addr_operand(oper->src[i]);
	}
	return new Operation<STORE>(oper->proc_opc,  data[0], addr[0], addr[1], addr[2]);
}

template<>
Node* make_operation<ADDR_OPER>(L_Oper* oper, L_Cb *cb)
{
	AddrOperand * src[2];
	AddrOperand * dest[1];
	for(int i = 0; i < 2; i++)
	{
		src[i] = build_addr_operand(oper->src[i]);
	}
	for(int i = 0; i < 1; i++)
	{
		dest[i] = build_addr_operand(oper->dest[i]);
	}
	return new Operation<ADDR_OPER>(oper->proc_opc, dest[0], src[0], src[1]);
}

template<>
Node* make_operation<ADDR_DATA>(L_Oper* oper, L_Cb* cb)
{
	AddrOperand * src[1];
	DataOperand * dest[1];
	for(int i = 0; i < 1; i++)
	{
		src[i] = build_addr_operand(oper->src[i]);
	}
	for(int i = 0; i < 1; i++)
	{
		dest[i] = build_data_operand(oper->dest[i]);
	}
	return new Operation<ADDR_DATA>(oper->proc_opc, dest[0], src[0]);
}

template<>
Node* make_operation<DATA_ADDR>(L_Oper* oper, L_Cb * cb)
{
	DataOperand *src[1];
	AddrOperand *dest[1];
	for(int i = 0; i < 1; i++)
	{
		src[i] = build_data_operand(oper->src[i]);
	}
	for(int i = 0; i < 1; i++)
	{
		dest[i] = build_addr_operand(oper->dest[i]);
	}
	return new Operation<DATA_ADDR>(oper->proc_opc, dest[0], src[0]);
}

template<>
Node* make_operation<JSR>(L_Oper* oper, L_Cb * cb)
{
	DataOperand * opd[1]; 
	opd[0] = build_data_operand(oper->src[0]);
	return new Operation<JSR>(oper->proc_opc, opd[0]);
}

template<>
Node* make_operation<JSR_SR>(L_Oper* oper, L_Cb* cb)
{
	DataOperand * opd[1];
	opd[0] = build_data_operand(oper->src[0]);
	return new Operation<JSR_SR>(oper->proc_opc, opd[0]);
}

template<>
Node* make_operation<BR_OPER>(L_Oper* oper, L_Cb *cb)
{
	DataOperand * src[3];
	for(int i = 0; i < 3; i++)
	{
		src[i] = build_data_operand(oper->src[i]);
	}
	Operation<BR_OPER> * op = new Operation<BR_OPER>(oper->proc_opc, src[0], src[1], src[2]);
	Set  live_var = L_get_oper_OUT_set(cb, oper, BOTH_PATHS);	
	int size = Set_size(live_var);
	int *buf = new int[size];
	Set_2array(live_var, buf);
	for(int i = 0; i < size; i++)
	{
		if(buf[i] % 2 == 1)
		{
			buf[i] = LM_convert_macro(buf[i]);
		}
		else
		{
			buf[i] = LM_convert_id(buf[i]);
		}
	}
	std::vector<int> ids(buf, buf+ size);
	delete[] buf;
	Set_dispose(live_var);
	op->build_implict_value(ids);
}

template<>
Node* make_operation<SIMD_STORE>(L_Oper* oper, L_Cb* cb)
{
	
	assert(bw_is_simd_start(oper));
	L_Oper * x = oper->next_op;
	while(!bw_is_simd_end(x))
	{
		x = x->next_op;
	}
	SimdOperand * data[1];
	AddrOperand * addr[3];
	//build SimdOperand
	data[0] = build_simd_operand(oper, x, 0, true);
	//build addr operand	
	L_Oper * first = oper->next_op;
	for(int i = 1; i < 4; i++)
	{
		addr[i-1] = build_addr_operand(oper->src[i]);
	}
	return new Operation<SIMD_STORE>(oper->proc_opc, data[0], addr[0], addr[1], addr[2]);
}

template< >
Node* make_operation<SIMD_LOAD>(L_Oper * oper, L_Cb *cb)
{
	assert(bw_is_simd_start(oper));
	L_Oper * x = oper->next_op;
	while(!bw_is_simd_end(x))
	{
		x = x->next_op;
	}
	SimdOperand * data[1];
	AddrOperand * addr[3];
	//build data operand
	data[0] = build_simd_operand(oper, x, 0, false);
	//build addr operand
	for(int i = 1; i < 4; i++)
	{
		addr[i-1] = build_addr_operand(oper->src[i]);
	}
	return new Operation<SIMD_LOAD>(oper->proc_opc, data[0], addr[0], addr[1], addr[2]);
}

template<>
Node* make_operation<SIMD_MATH>(L_Oper * oper, L_Cb * cb)
{
	SimdOperand * src[2];
	SimdOperand * dest[1];
	assert(bw_is_simd_start(oper));
	L_Oper * end = oper;
	while(bw_is_simd_end(end))
	{
		end = end->next_op;	
	}
	for(int i = 0; i < 1; i++)
	{
		dest[i] = build_simd_operand(oper, end, i, false);
	}
	for(int i = 0; i < 2; i++)
	{
		src[i] = build_simd_operand(oper, end, i, true);
	}
	return new Operation<SIMD_MATH>(oper->proc_opc, dest[0], src[0], src[1]);	
}

template<>
Node* make_operation<START_NODE>(L_Oper * oper, L_Cb * cb)
{
	Set live_var = L_get_cb_IN_set(cb);
	int size = Set_size(live_var);
	int * buf = new int[size];
	Set_2array(live_var, buf); 
	for(int i = 0; i< size; i++)
	{
		if(buf[i]%2 == 1)
		{ 
			buf[i] =LM_convert_macro(buf[i] >>1);
		}
		else
		{
			buf[i] = LM_convert_id(buf[i]>>1);
		}
	}
	std::vector<int> ids(buf, buf + size);
	delete[] buf;
	Set_dispose(live_var);
	return new Operation<START_NODE>(ids);
}

template<>
Node* make_operation<END_NODE>(L_Oper* oper, L_Cb *cb)
{
	Set live_var = L_get_cb_OUT_set(cb);
	int size = Set_size(live_var);
	int *buf = new int[size];
	Set_2array(live_var, buf);
	for(int i = 0; i < size; i++)
	{
		if(buf[i] % 2 == 1)
		{
			buf[i] = LM_convert_macro(buf[i] >> 1);
		}
		else
		{
			buf[i] = LM_convert_id(buf[i] >> 1);
		}
	}
	std::vector<int> ids(buf, buf + size);
	delete[] buf;
	Set_dispose(live_var);	
	return new Operation<END_NODE>(ids);
}

unsigned int  get_style(L_Oper * oper )
{
	if(bw_is_simd_load(oper))
		return SIMD_LOAD;
	if(bw_is_simd_store(oper))
		return SIMD_STORE;
	if(bw_is_simd_start(oper))
		return SIMD_MATH;
//not simd operation
	int proc_opc = oper->proc_opc;
	if(proc_opc == LBWop_MOV_ADDR_GR )
		return ADDR_DATA;

	if(proc_opc == LBWop_MOV_GR_ADDR )
		return DATA_ADDR;
	if(proc_opc == LBWop_MOV_GR_SR)
	{
		return DATA_SR;
	}
	if(proc_opc == LBWop_JSR_SR)
		return JSR_SR;
	if(proc_opc == LBWop_JSR)
		return JSR;
	if(proc_opc == LBWop_RTS)
		return RTS;
	if(proc_opc == LBWop_ADD_ADDR_IMM8 ||
		proc_opc == LBWop_ADD_ADDR ||
		proc_opc == LBWop_MOV_IMM32_ADDR
		)
			return ADDR_OPER;
	if(proc_opc == LBWop_LD ||
		proc_opc == LBWop_LD_IMM16_IMM7 ||
		proc_opc == LBWop_LD_IMM16 ||
		proc_opc == LBWop_LD_IMM7
		)
		return LOAD;
	if(proc_opc == LBWop_ST||
		proc_opc == LBWop_ST_IMM16_IMM7 ||
		proc_opc == LBWop_ST_IMM16 ||
		proc_opc == LBWop_ST_IMM7
		)
		return STORE;
	//may be not accurate
	if(proc_opc == LBWop_COMB_U
		||proc_opc == LBWop_COMB_S
		||proc_opc == LBWop_COMB_F
		)
		return BR_OPER;
	return NORMAL_OPER;
}



/*
 * the oper is reference , so the oper may be changed for some simd operation
 */
Node * LcodeBlockImpl::new_node(L_Oper * &oper)
{
	unsigned int  t = get_style(oper);
	switch (t)
	{
	case START_NODE:
	case END_NODE:
		assert("new node" ==  "START_NODE END_NODE");
		return NULL;
	case LOAD:
		return make_operation<LOAD>(oper, cb);
	case STORE:
		return make_operation<STORE>(oper, cb);
	case ADDR_OPER:
		return make_operation<ADDR_OPER>(oper, cb);
	case ADDR_DATA:
		return make_operation<ADDR_DATA>(oper, cb);
	case DATA_ADDR:
		return make_operation<DATA_ADDR>(oper, cb);
	case NORMAL_OPER:
		return make_operation<NORMAL_OPER>(oper, cb);
	case DATA_SR:
		return make_operation<DATA_SR>(oper, cb);
	case JSR:
		return make_operation<JSR>(oper, cb);
	case JSR_SR:
		return make_operation<JSR_SR>(oper, cb);
	case RTS:
		return make_operation<RTS>(oper, cb);
	case BR_OPER:
		return make_operation<BR_OPER>(oper, cb);
	case SIMD_LOAD:
		{
			Node * n =  make_operation<SIMD_LOAD>(oper, cb);
			bw_skip_simd_oper(oper);
			return n;
		}
	case SIMD_STORE:
		{
			Node * n = make_operation<SIMD_STORE>(oper, cb);
			bw_skip_simd_oper(oper);
			return n;
		}
	case SIMD_MATH:
		{
			Node * n = make_operation<SIMD_MATH>(oper, cb);
			bw_skip_simd_oper(oper);
			return n;
		}
	default:
		assert("error" == "unexpected operation");
	}
}


LcodeBlockImpl::LcodeBlockImpl(L_Cb * cb): cb(cb)
{

}

#ifdef __cplusplus
extern "C"{
#endif
extern Set L_get_cb_IN_set(L_Cb* cb);
extern Set L_get_cb_OUT_set(L_Cb*cb);
#ifdef __cplusplus
}
#endif

Node * LcodeBlockImpl::build_startnode()
{
	Set set = L_get_cb_IN_set(cb);
	int size = Set_size(set);
	int * buf = new int[size];
	Set_2array(set, buf);
	std::vector<int> live_in(buf, buf+size);
	delete[] buf;
	Node *startnode = new Operation<START_NODE>(live_in);
	return startnode;
}


Node * LcodeBlockImpl::build_endnode()
{
	Set set = L_get_cb_OUT_set(cb);
	int size = Set_size(set);
	int * buf = new int[size];
	Set_2array(set, buf);
	std::vector<int> live_out(buf, buf + size);
	delete[] buf;
	Set_dispose(set);
	Node * endnode = new Operation<END_NODE>(live_out);
	return endnode;
}

void LcodeBlockImpl::build_nodelist(NodeSequence &nodelist)
{
	L_Oper * oper = NULL;
	for(oper = cb->first_op; oper != NULL; oper = oper->next_op)
	{
		if(bw_is_ignored_oper(oper))
			continue;
		//note: the simd_oper is skip by the L_Oper *& argument!!!
		Node * n = new_node(oper);
		nodelist.push_back(n);
	}
}


